package Measurements;

import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Vector;

import au.com.bytecode.opencsv.CSVWriter;
import Basics.*;
import Derivation.SemiNaive;
import Parsing.*;
import Pattern.*;

public class OfflineMeasurements 
{
	SemiNaive sn;
	
	public OfflineMeasurements () {}

	
	
	/*************************************************************************************************************/
	/** Title: MeasureAndWriteCSV																	
	/** Description: Apply's the function of the class and writes the result in CSV file  						
	/*************************************************************************************************************/
	
	public void MeasureAndWriteCSV (String path) throws IOException
	{
		DB.getInstance().DropTableIfExists("imports");
		DB.getInstance().DropTableIfExists("exports");
		DB.getInstance().DropTableIfExists("dealsWith");
		
		FileWriter fw = new FileWriter(path);
		CSVWriter writer = new CSVWriter(fw);
		ArrayList<String[]> data = new ArrayList<String[]>();
		data.add(new String[] {"Size of initial DB", "Duration for prov. generation in seconds", "Number of atoms in provenance",
				"Duration for intersection in seconds", "Number of rules added in intersection", "Number of instantiated rules in provenance", "Size of DB After Iteration"});
		
		writer.writeAll(data);
		DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
		for (int i = 100; i < 600; i+=100) 
		{
			DB.getInstance().DropTableIfExists("imports");
			DB.getInstance().DropTableIfExists("exports");
			DB.getInstance().DropTableIfExists("dealsWith");
			ParseDB rfDB = new ParseDB ("yago2core_facts.clean.notypes.tsv", i);//C:\\Users\\amirgilad\\Downloads\\
			
			String[] array1 = this.MeasureFullProv();
			String[] array2 = this.MeasureIntersectionWithFullProv();
			String[] array1and2 = new String[array1.length + array2.length + 1];
			System.arraycopy(array1, 0, array1and2, 1, array1.length);
			System.arraycopy(array2, 0, array1and2, array1.length + 1, array2.length);
			array1and2[0] = Integer.toString(i);
			writer.writeNext(array1and2);
			writer.flush();
			System.out.println("---------------------------------------------------------------------------------------------------------------------------------");
			System.out.println("Last Iteration Finished at: " + dateFormat.format(Calendar.getInstance().getTime()));
			System.out.println(String.format("Size of initial DB: %d\nDuration for prov. generation in seconds: %s\nNumber of atoms in provenance: %s\nDuration for intersection in seconds: %s\n" 
				+ "Number of rules added in intersection: %s\nNumber of instantiated rules in provenance: %s\nSize of DB After Iteration: %s", i, array1[0], array1[1], array2[0], array2[1], array2[2], array2[3]));
			System.out.println("---------------------------------------------------------------------------------------------------------------------------------");
		}
		
	    writer.close();
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MeasureFullProv																					
	/** Description: Measures the time and size of Full Prov. via semiNaive									
	/*************************************************************************************************************/
	
	public String[] MeasureFullProv ()
	{
		Program p = MakeProgram ();
		long startTime = System.currentTimeMillis();
		this.sn = new SemiNaive(p);
		sn.Run(true);									//with intersection
		long endTime = System.currentTimeMillis();
		double durationFullProv = (endTime - startTime)/1000;
		
		long numOfAtoms = sn.getParser().getProvenance().values().size();
		
		return new String[] {Double.toString(durationFullProv), Long.toString(numOfAtoms)};
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MeasureIntersectionWithFullProv																					
	/** Description: Measures the time of Intersection With Full Prov. and the size of intersected rules									
	/*************************************************************************************************************/
	
	public String[] MeasureIntersectionWithFullProv ()
	{
		Pattern pattern = MakePattern();
		Program p = new Program(this.sn.getParser().getConstantsProgram());
		IntersectWithProgram iwp = new IntersectWithProgram (p, pattern);
		long sieOfOrigProgram = p.getRules().size();
		long startTime = System.currentTimeMillis();
		iwp.getPattern().Renaming();
		iwp.IntersectNoTransitives();
		long endTime = System.currentTimeMillis();
		
		double intersectionTime = (endTime - startTime)/1000;
		long numOfRules = this.sn.getParser().getConstantsProgram().size();
		long numOfRulesAdded = iwp.getP().getRules().size() - sieOfOrigProgram;
		
		return new String[] {Double.toString(intersectionTime), Long.toString(numOfRulesAdded), Long.toString(numOfRules), Long.toString(DB.getInstance().Size())};
	}
	
	
	
	
	/*************************************************************************************************************/
	/** Title: MakePattern																	
	/** Description: Builds the pattern   						
	/*************************************************************************************************************/
	
	public Pattern MakePattern ()
	{
		////////////////////////////////////////////////
		Constant Canada = new Constant("Canada", "Country");
		Constant Andorra = new Constant("Andorra", "Country");
		PatternNode root = new PatternNode ("dealsWith",false, Canada, Andorra);
		PatternNode child1 = new PatternNode ("dealsWith",false, Andorra, Canada);
		root.setChildren(child1);
		child1.setParent(root); 

		Vector<PatternNode> rootVec = new Vector<PatternNode> ();
		rootVec.add(root);

		Vector<PatternNode> childVec = new Vector<PatternNode> ();
		childVec.add(child1);

		Vector<Vector<PatternNode>> pattern = new Vector<Vector<PatternNode>> ();
		pattern.add(rootVec); 
		pattern.add(childVec); 
		////////////////////////////////////////////////
		
		return new Pattern(pattern);
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MakeProgram																					
	/** Description: Builds the program out of string									
	/*************************************************************************************************************/
	
	public Program MakeProgram ()
	{
		String input = "dealsWith(a, b):- imports(a, c), exports(b, c) & 0.8"
				+ " \n dealsWith(a, b):- dealsWith(a, f), dealsWith(f, b) & 0.5 "
				+ "\n dealsWith(a, b):- dealsWith(b, a) & 1";
		ParseProgram pp = new ParseProgram(input);
		return pp.getProgram();
	}
}
